package io.httpprox;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpResponse;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.log4j.Logger;

/**
 * 
 * @author tzj
 *
 */
@Deprecated
public class HttpProx extends HttpServlet {
	private static final long serialVersionUID = 1L;

	Logger logger = Logger.getLogger(getClass());

	
	ScheduledExecutorService executorService = Executors.newScheduledThreadPool(8);
	
	HttpClientBuilder builder = HttpClientBuilder.create();
	
	final int  buffsize=8192;
	
	final int maxcheckfiletimes=10;
	
	 String cachedir;
	
	String cacheFiles[];
	
	final String[] notmodifyresource={".css",".js"};
	
	public HttpProx() {

		PoolingHttpClientConnectionManager clientConnectionManager = new PoolingHttpClientConnectionManager();
		clientConnectionManager.setMaxTotal(50);
		clientConnectionManager.setDefaultMaxPerRoute(30);
		clientConnectionManager.closeIdleConnections(5, TimeUnit.MINUTES);
		builder.setConnectionManager(clientConnectionManager);
		builder.setRedirectStrategy(new LaxRedirectStrategy());
		
		
	}
	
	

	
	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		cachedir=config.getServletContext().getInitParameter("cachedir");
		cacheFiles=config.getServletContext().getInitParameter("cachefile").split(",");
	}




	/**
	 * 处理get请求
	 */
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		
		StringBuilder stringBuilder = new StringBuilder(req.getRequestURI());
		String query = req.getQueryString();
		boolean cacheit=false;
		
		if (query != null)
			stringBuilder.append("?" + query);
		
		
		if (this.getInitParameter("mapurl")!=null) {

			final String host = this.getInitParameter("mapurl");
			final String uri = stringBuilder.toString();
			int code = (host + uri).hashCode();
			boolean readcached=false;

			 logger.info("get"+uri);
			for (int i = 0; i < cacheFiles.length; i++) {
				if(req.getRequestURI().endsWith(cacheFiles[i])){
					cacheit=true;
					break;
				}
			}
			
			
			if(cacheit){
				resp.setDateHeader("Expires",System.currentTimeMillis()+3600*1000*24);
				resp.setDateHeader("Last-Modified",new Date().getTime());
				SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				System.out.println(dateFormat.format(req.getDateHeader("If-Modified-Since")));
				int times=0;
				
				File cachefile=new File(cachedir+"/" + code);
				for (int i = 0; i < notmodifyresource.length; i++) {
					if(uri.endsWith(notmodifyresource[i])
							&&cachefile.isFile()){
						resp.setHeader("Cache-Control","max-age=3600, must-revalidate");
						break;
					}
				}
				
				while(maxcheckfiletimes>times
						&&!(readcached=copyFile2Resp(cachefile, resp))){
					times++;
					
					 logger.info("cache"+uri);
					 
					 long  time1=System.currentTimeMillis();
					cacheFile(host, uri);
					long  time2=System.currentTimeMillis();
					
					logger.info("cache"+uri+"耗时"+(time2-time1));
				}
				
			}
			
			if(!readcached){
				 long  time1=System.currentTimeMillis();
				 
					doProx(req, resp, host, uri, true);
					 long  time2=System.currentTimeMillis();
					 logger.info("prox"+uri+"耗时"+(time2-time1));
			}
			
			
		}

	}
	/**
	 * 处理post请求
	 */
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		if (this.getInitParameter("mapurl")!=null)  {
			doProx(req, resp, this.getInitParameter("mapurl"), req.getRequestURI(), false);
		}

	}

	/**
	 * 代理处理
	 * 
	 * @param req
	 * @param resp
	 * @param requesturl
	 * @param isget
	 * @throws IOException
	 */
	private void doProx(HttpServletRequest req, HttpServletResponse resp, final String host, final String uri,
			boolean isget) throws IOException {

		HttpClient client = builder.build();
		HttpClientContext clientContext = builderClientContext();
		HttpRequestBase request = null;
		if (isget) {
			request = new HttpGet(host + uri);
		} else {
			HttpPost httpPost = new HttpPost(host + uri);
			httpPost.setEntity(
					new InputStreamEntity(req.getInputStream(),
					ContentType.create(req.getContentType())));
			request = httpPost;
		}
		
		addCookie(req, request);
		HttpResponse httpResponse = client.execute(request,clientContext);
		List<Cookie> rescookies = clientContext.getCookieStore().getCookies();

		for (Cookie cookie : rescookies) {
			javax.servlet.http.Cookie cookie2 = null;
			cookie2 = new javax.servlet.http.Cookie(cookie.getName(), cookie.getValue());
			cookie2.setPath(cookie.getPath());
			resp.addCookie(cookie2);
		}
		
		InputStream inputStream = httpResponse.getEntity().getContent();
		int b = -1;
		byte[] bs = new byte[buffsize];
		while ((b = inputStream.read(bs)) != -1) {
			resp.getOutputStream().write(bs, 0, b);
		}
		
		
		resp.getOutputStream().flush();
		request.releaseConnection();
	}

	/**
	 * 添加cookie
	 * 
	 * @param request
	 * @param httpRequestBase
	 */
	private void addCookie(HttpServletRequest request, HttpRequestBase httpRequestBase) {

		javax.servlet.http.Cookie[] reqcookies = request.getCookies();

		if (reqcookies != null) {
			StringBuilder stringBuilder = new StringBuilder();

			for (int i = 0; i < reqcookies.length; i++) {
				if (i > 0) {
					stringBuilder.append(";");
				}

				stringBuilder.append(reqcookies[i].getName() + "=" + reqcookies[i].getValue());
			}
			httpRequestBase.addHeader("Cookie", stringBuilder.toString());
		}

	}

	/**
	 * 构建clientcontext
	 * 
	 * @return
	 */
	private HttpClientContext builderClientContext() {

		CookieStore cookieStore = new BasicCookieStore();
		HttpClientContext context = HttpClientContext.create();
		context.setCookieStore(cookieStore);
		
		return context;

	}

	/**
	 * cache input的数据
	 * 
	 * @param request
	 * @param inputStream
	 * @throws IOException
	 */
	private void cacheFile(String host, String uri) throws IOException {

		int urlencode = (host + uri).hashCode();
		final File tempdfile = new File(cachedir+ "/" + urlencode);

		File parentFile = tempdfile.getParentFile();

		RandomAccessFile randomAccessFile=null;
		if (!parentFile.isDirectory())
			parentFile.delete();
		if (!parentFile.exists())
			parentFile.mkdirs();

		if (parentFile.exists())
			randomAccessFile = new RandomAccessFile(tempdfile, "rws");
				
		if (randomAccessFile != null) {
			
			FileLock fileLock=null;
			boolean locked=false;
			try {
				 fileLock=randomAccessFile.getChannel().tryLock();
				 locked=true;
			} catch (OverlappingFileLockException e) {
				logger.warn("重复锁定",e);
			}
			
			if(locked){
				
				HttpClient client=builder.build();
				HttpGet get=new HttpGet(host+uri);
				int b = -1;
				byte[] bs = new byte[buffsize];
				InputStream inputStream = client.execute(get).getEntity().getContent();
				while ((b = inputStream.read(bs)) != -1) {
					randomAccessFile.write(bs, 0, b);
				}
				fileLock.release();	
				randomAccessFile.close();
				get.releaseConnection();
				
				executorService.schedule(new Runnable() {
					@Override
					public void run() {
						while(!tempdfile.delete()){
							try {
								Thread.sleep(10);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						logger.info("删除缓存文件"+tempdfile+"成功");
					}
				}, 1, TimeUnit.DAYS);
			}
		}

	}

	
	/**
	 * 
	 * @param file
	 * @param response
	 * @return
	 * @throws IOException
	 */
	private  boolean  copyFile2Resp(File file,HttpServletResponse response) throws IOException{
		

		if(file.isFile()){
			OutputStream outputStream=response.getOutputStream();
			FileInputStream fileInputStream=new FileInputStream(file);
			int b=-1;
			byte[] bs=new byte[buffsize];
			while((b=fileInputStream.read(bs))!=-1){
				outputStream.write(bs, 0, b);
			}
			fileInputStream.close();
			response.flushBuffer();
			return true;
		}
		return false;
	}

}
